//
//  ViewController.m
//  GCD
//
//  Created by admin on 2019/8/17.
//  Copyright © 2019 猫爷爷的酱醋茶. All rights reserved.
//


/*
 
 GCD
     a）旨在替代NSThread等线程技术，充分利用设备的多核
     b）使用C语言编写，本质是函数
     c）线程生命周期自动管理，经常使用
 
    GCD是苹果公司为多核的并行运算提出的解决方案
    GCD会自动利用更多的CPU的内核（比如双核、四核）
    GCD会自动管理线程生命周期（创建线程、调度任务、销毁线程）
 
 两大核心：
    任务：执行什么操作
    队列：用来存放任务
 
 使用：
    a）创建任务
    b）将任务添加到队列中
        GCD会自动将队列中的任务取出，放到对应的线程中执行
        任务的取出遵循队列的FIFO原则：先进先出，后进后出
     - 异步：具有开启新线程的能力，在新的线程中执行任务
     - 同步：没有开启新线程的能力，在当前线程中执行
    - 并发任务（只能在异步下执行）
 
    - 串行任务（按顺序一个一个执行）
 
 
 
 主队列 - 全局串行队列
  先执行完主线程上的代码，才会执行主队列中的任务
 
 全局队列 - 本质是全局并发队列
    和并发队列的区别：
    a）并发队列有名称，可以跟踪错误，而全局队列没有
    b）全局队列不管实在ARC还是MRC下都不需要考虑释放内存，release只有一个
       而并发队列在ARC中不需要考虑释放内存，而是MRC中需要手动释放内存，并发队列create创建出来的，就要release
 
    dispatch_queue_t queueX = dispatch_queue_create("xxx",DISPATCH_QUEUE_CONCURRENT); //创建
    dispath_release(queueX);
 
 
 同步任务的作用：
    在网络开发中，通常会把很多任务放在后台异步执行，但是有些任务彼此依赖，这时候就需要同步来约束顺序执行。例：[self analogNetwork];
 
 
 
 Barrier 阻塞
 
    主要作用于在多个异步操作完成之后，统一对非线程安全的对象进行更新。
 
    适合于大规模的I|O操作
 
    当访问数据库或文件的时候，更新数据的时候不能和其他更新或读取的操作在同一时间执行，可以使用调度组，不过有点复杂
    可以使用 dispatch_barrier_async解决。
 
 
 */
#import "ViewController.h"

@interface ViewController ()
@property(nonatomic,strong)UIScrollView *scrollView;
@property(nonatomic,strong)UIImageView *imageView;

@property(nonatomic,strong)NSMutableArray *photoList;

@end

@implementation ViewController

//懒加载
- (NSMutableArray *)photoList
{
    if (_photoList == nil)
    {
        _photoList = [NSMutableArray array];
    }
    return _photoList;
}

//异步下载图片
//- (void)loadView
//{
//    self.scrollView = [[UIScrollView alloc]initWithFrame:[UIScreen mainScreen].bounds];
//    self.scrollView.backgroundColor = [UIColor whiteColor];
//    self.view = self.scrollView;
//
//    self.imageView = [[UIImageView alloc]init];
//    [self.scrollView addSubview:self.imageView];
//}


- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    
    //
    
    [self dispatchGroup];
//    for (int i = 1; i<= 10; i++)
//    {
//        [self downLoadImage:i];
//    }
    
}

#pragma mark - 模拟从网络上下载很多张图片，并且把下载完成的图片添加到MutableArray中
- (void)downLoadImage:(int)index
{
    //模拟下载图片
    NSString *fileName = [NSString stringWithFormat:@"%02d.jpg",index];
    NSString *path = [[NSBundle mainBundle]pathForResource:fileName ofType:nil];
    UIImage *image = [UIImage imageWithContentsOfFile:path];
    
    //把图片添加到可变数组中
    [self.photoList addObject:image];
    NSLog(@"图片下载完成 %@ %@",fileName,[NSThread currentThread]);
}

//- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
//{
//    NSLog(@"%zd",self.photoList.count);
//}

#pragma mark - 调度组
-(void)dispatchGroup
{
    //创建一个线程组
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"下载第一首歌");
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"下载第二首歌");
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSLog(@"下载第三首歌");
    });
    
    //等待组内的所有任务都完成，才调度
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"下载完成");
    });
    
    //等待组内的任务都执行完成，才会继续执行后续的代码
//    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
//
//    NSLog(@"播放音乐");
    
    
}

#pragma mark - 一次执行
-(void)dispatchOnce
{
    //无论循环几次，都只执行一次
    for (int i = 0; i< 10; i++)
    {
        //执行在当前线程上
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"加油~ 猫爷爷的酱醋茶 %@",[NSThread currentThread]);
        });
    }
    
}

#pragma mark - 延迟执行
-(void)dispatchAfter
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"延迟执行");
    });
}

#pragma mark - 模拟网络同步
//AppStore - 验证密码->支付->下载应用
-(void)analogNetwork
{
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"验证密码 %@",[NSThread currentThread]);
        });
        
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"支付 %@",[NSThread currentThread]);
        });
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"下载应用 %@",[NSThread currentThread]);
        });


    });
    
   
//        //线程的执行是无法确认的，会造成顺序混乱
//        dispatch_async(dispatch_get_global_queue(0, 0), ^{
//            NSLog(@"验证密码");
//        });
//
//        dispatch_async(dispatch_get_global_queue(0, 0), ^{
//            NSLog(@"支付");
//        });
//
//        dispatch_async(dispatch_get_global_queue(0, 0), ^{
//            NSLog(@"下载应用");
//        });
    
   

    
}

#pragma mark - 主队列(同步) - 解决死锁
-(void)syncMainQueueAnd
{
    //通过循环创建不同的任务
    NSLog(@"Begin~");
    
    //开辟一个子线程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for (int i = 0; i < 10 ; i++)
        {
            //执行完毕后回到主线程
            dispatch_sync(dispatch_get_main_queue(), ^{
                NSLog(@"猫爷爷的酱醋茶，加油赢取更多的能力！ %d %@",i,[NSThread currentThread]);
            });
            
        }
    });
    
    NSLog(@"End");
}

#pragma mark - 主队列(异步)
//先执行完主线程上的代码，才会执行主队列中的任务，所有先输出 Begin~ 和 End~
-(void)asyncMainQueue //顺序执行，不开启新线程
{
    //通过循环创建不同的任务
    NSLog(@"Begin~");
    for (int i = 0; i < 10 ; i++)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"猫爷爷的酱醋茶，加油赢取更多的能力！ %d %@",i,[NSThread currentThread]);
        });
        
    }
    NSLog(@"End");
}


#pragma mark - 主队列(同步) - 死锁
-(void)syncMainQueue
{
    //通过循环创建不同的任务
     NSLog(@"Begin~");
    for (int i = 0; i < 10 ; i++)
    {
       //主线程上会造成死锁  主线程执行到此，等待dispatch_get_main_queue()方法执行，但是该方法又等待主线程执行，相互等待。
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"猫爷爷的酱醋茶，加油赢取更多的能力！ %d %@",i,[NSThread currentThread]);
        });
        
    }
    NSLog(@"End");
}

#pragma mark - 并行队列(异步)
-(void)asyncConcurrent //开启新线程 无序执行
{
    //通过循环创建不同的任务
    for (int i = 0; i < 10 ; i++)
    {
        //把任务加载到队列中，不需要关系开辟线程，GCD会操作
        dispatch_async(dispatch_queue_create("silent", DISPATCH_QUEUE_CONCURRENT), ^{
            NSLog(@"猫爷爷的酱醋茶，加油赢取更多的能力！ %d %@",i,[NSThread currentThread]);
        });
    }
}

#pragma mark - 并行队列（同步）
-(void)syncConcurrent //按顺序执行，不开启新的线程，只在当前线程上执行
{
    //通过循环创建不同的任务
    for (int i = 0; i < 10 ; i++)
    {
        dispatch_sync(dispatch_queue_create("silent", DISPATCH_QUEUE_CONCURRENT), ^{
            NSLog(@"猫爷爷的酱醋茶，加油赢取更多的能力！ %d %@",i,[NSThread currentThread]);
        });
    }
}

#pragma mark - 串行队列（异步）
-(void)asyncSerial //开启新线程 无序执行
{
    /*
     参数一：队列的名字
     参数二：队列为串行
     */
    
    //通过循环创建不同的任务
    for (int i = 0; i < 10 ; i++)
    {
        dispatch_async(dispatch_queue_create("Silent", DISPATCH_QUEUE_SERIAL), ^{
            NSLog(@"猫爷爷的酱醋茶，加油赢取更多的能力！ %d %@",i,[NSThread currentThread]);
            
        });
    }
}

#pragma mark - 串行队列（同步）
-(void)syncSerial //按顺序执行，不开启新的线程，只在当前线程上执行
{
    /*
     参数一：队列的名字
     参数二：队列为串行
     */
    
     //通过循环创建不同的任务
    for (int i = 0; i < 10 ; i++)
    {
        dispatch_sync(dispatch_queue_create("Silent", DISPATCH_QUEUE_SERIAL), ^{
            NSLog(@"猫爷爷的酱醋茶，加油赢取更多的能力！ %d %@",i,[NSThread currentThread]);
            
        });
    }
    
}

#pragma mark - 异步下载网络图片
-(void)downloadImageWithAsync
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1566409677&di=74e2b4870f47befc92e7e5c14feca2bf&imgtype=jpg&er=1&src=http%3A%2F%2Fimg.app178.com%2Ftu%2F201410%2Fxjgpfwrdpqy.jpg"];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage imageWithData:data];
        
        //回到主线程 更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            
            self.imageView.image = image;
            [self.imageView sizeToFit ];
            
            self.scrollView.contentSize = image.size;
            
        });
        
    });
}




- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    //间隔一段时间点击屏幕，会发现线程变动，这是因为在任务执行完成之后，线程会在线程池保留一段时间，超过这个时间，自动销毁
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"猫爷爷的酱醋茶是IOS开发者！%@",[NSThread currentThread]);
    });
}


@end
